#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "Scanner.h"

char *comment[] = { "/*", "*/" };

int curTableCount = 0;

char *tokenLookup[] = {"", "nul", "[a-z]+([a-z]|[0-9])+", "[0-9]+", "+", "-", "*", "/", "odd", "=", "#", "<", "$", ">", "%", "(", ")", ",", ";", ".", ":=", "{", "}", "if", "then", "while", "do", "call", "const", "int", "procedure", "write", "[", "]"};

char *word[] = {  "null", "begin", "call", "const", "do", /*"else",*/ "end", "if",
	              "odd", "procedure", /*"read",*/ "then", /*"var",*/ "while", "write"};

/* internal representation  of reserved words */
int  wsym[] =  { nulsym, beginsym, callsym, constsym, dosym, /*elsesym,*/ endsym, ifsym,
                   oddsym, procsym, /*readsym,*/ thensym, /*varsym,*/ whilesym, writesym};

int IsLetter(char c)
{
    return ((int)c >= 65 && (int)c <= 90)  ||
           ((int)c >= 97 && (int)c <= 122);
}

int IsDigit(char c)
{
    return (int)c >= 48 && (int)c <= 57;
}

int GetSpecialSym(char c)
{
    return ssym[(int)c];
}

int GetReservedWordSym(char c[tempmax])
{
    int i;
    for(i = 0; i < norw; i++)
    {
        if(strcmp(c, word[i]) == 0)
            return wsym[i];
    }

    return 0;
}

/*int GetArrayValue(char curToken[tempmax])
{
    int result;
    int i = 0;

    char ch = curToken[i];
    while( ch != '[' )
    {
        if(i >= cmax)
            return -1;
        i++;
        ch = curToken[i];
    }
    if(!IsDigit(ch))
        return -1;
    else
        return atoi(&ch);
}*/

/*int ArrayCheck(char curToken[tempmax])
{
    int result;
    int i = 0;
    /*state 1*
    char ch = curToken[i];
    if(IsLetter(ch))
    {
        /*state 2*
 	    while( IsLetter(ch) || IsDigit(ch))
        {
            if(i >= cmax)
                return identover;
            if(ch == '[')
            {
                i++;
                ch = curToken[i];
                break;
            }
            i++;
            ch = curToken[i];
		}
        /*state 3*
        if(IsDigit(ch))
        {
            i++;
            ch = curToken[i];
            if(ch == ']' )
                result = array;
            else
                return notarray;

        }
        else
            return notarray;
        //token = (identsym, index in ST);
		return result;
	}
    else
    {
        return notletter;    // look for a different token
    }
}*/

//If a string at the position indicated by i(inside tokens[]) contains two tokens, this
//function will split token into two or more tokens in the tokens array
int SplitSpecialChar(int i)
{
    char temp[tempmax];
    char temp2[tempmax];
    strcpy(temp, tokens[i]);

    int j = 0;
    if((IsLetter(temp[j]) || IsDigit(temp[j]) || GetSpecialSym(temp[j]) != 0) && temp[j] != ':')
    {
        for(j = 1; j < tempmax; j++)
        {
            if(temp[j] == '\0')
                break;
            if(GetSpecialSym(temp[j]) != 0)
            {
                strncpy(temp2, temp, j);
                temp2[j] = '\0';
                strcpy(tokens[i], temp2);
                i++;
                strncpy(tokens[i], &temp[j], 1);
                if(temp[j+1] != '\0')
                {
                    strncpy(temp, temp + (j+1), cmax-1);
                    i++;
                }
                else
                    break;
                //break;
            }
        }
    }

    return i;
}

//Reads the data from the file and determines what to do with each token
void ReadFile()
{
    int i = 0;
    int isComment = 0;
    while(!feof(inputStreamScanner))
    {
        //Split everything up into tokens
        fscanf(inputStreamScanner, "%s", tokens[i]);
        //Split out any special characters from the current token
        i = SplitSpecialChar(i);
        if(!isComment)
        {
            if(strcmp(tokens[i], comment[0]) != 0)
            {
                i++;
            }
        }
        else
        {
            if(strcmp(tokens[i], comment[1]) != 0)
            {
                i++;
            }
        }
    }

    TOKEN_LENGTH = i;

    int j;
    for(j = 0; j < TOKEN_LENGTH; j++)
    {
        if(tokens[j] != "")
            DetermineTokenType(j, tokens[j]);
    }
}

//Outputs the code to the file
void OutputCode(FILE* out)
{
    fprintf(out, "%s", "Source Program:\n");
    int i;
    for(i = 0; i < TOKEN_LENGTH; i++)
    {
        if(curLevel > 0)
        {
            int j;
            for(j = 0; j < curLevel; i++)
            {
                fprintf(out, "\t");
            }
        }
        fprintf(out, "%s ", tokens[i]);
        if(strcmp(tokens[i], tokenLookup[semicolonsym]) == 0 )
            fprintf(out, "\n");

    }
}

void OutputLexemeTable(FILE* out)
{
    fprintf(out, "Lexeme Table:\n");
    fprintf(out, "Lexeme \t\t Token Type\n");

    int i;
    for(i = 0; i < TOKEN_LENGTH; i++)
    {
        if(strlen(tokens[i]) >= 6)
            fprintf(out, "%s \t %d\n", tokens[i], tokenType[i]);
        else if(strlen(tokens[i]) >= 3)
            fprintf(out, "%s \t\t %d\n", tokens[i], tokenType[i]);
        else
            fprintf(out, "%s \t\t\t %d\n", tokens[i], tokenType[i]);
    }
}

void OutputLexemeList(FILE* out)
{
    fprintf(out, "Lexeme List:");

    int i;
    for(i = 0; i < TOKEN_LENGTH; i++)
    {
        if(tokenType[i] == identsym || tokenType[i] == numbersym)
            fprintf(out, "%d %s ", tokenType[i], tokens[i]);
        else
            fprintf(out, "%d ", tokenType[i]);
    }
}

int IdentifierCheck(char curToken[tempmax])
{
    int result;
    int i = 0;
    /*state 1*/
    char ch = curToken[i];
    if(IsLetter(ch))
    {
        /*state 2*/
 	    while( IsLetter(ch) || IsDigit(ch) )
        {
            if(i >= cmax)
                return identover;
            i++;
            ch = curToken[i];
		}
        /*state 3*/
        //if(ch == '[')
        //    result = ArrayCheck(curToken);
        //else
        i--;  // we have scanned one character too far
        result = valid;
        //token = (identsym, index in ST);
		return result;
	}
    else
    {
        return notletter;    // look for a different token
    }
}

int NumberCheck(char curToken[tempmax])
{
    int result;
    int i = 0;
    /*state 4*/
    char ch = curToken[i];
	if( IsDigit(ch) )
    {
        int value = atoi(&ch);
        /*state 5*/
        i++;
        ch = curToken[i];
        while( IsDigit(ch) )
        {
            if(i >= nmax)
                return numover;
            value = 10 * value + atoi(&ch);
            i++;
	        ch = curToken[i];
	    }
        /*state 6*/
        i--;
	    //token = (numbersym, value)
        result = valid;
	    return result;
	}
    /*state 7*/
    else
    {
        return invalidsym;  //look for a different token
    }
}

void InitializeScanner()
{
    curLevel = 0;
    ssym['+']=plussym;
    ssym['-']=minussym;
    ssym['*']=multsym;
    ssym['/']=slashsym;
    ssym['(']=lparensym;
    ssym[')']=rparensym;
    ssym['=']=eqsym;
    ssym[',']=commasym;
    ssym['.']=periodsym;
    ssym['#']=neqsym;
    ssym['<']=lessym;
    ssym['>']=gtrsym;
    ssym['$']=leqsym;
    ssym['%']=geqsym;
    ssym[';']=semicolonsym;
    ssym['{']=beginsym;
    ssym['}']=endsym;
    ssym['[']=lbracketsym;
    ssym[']']=rbracketsym;
    int i = 0;
    for(i = 0; i < MAX_NAME_TABLE_SIZE; i++)
    {
        table[i] = malloc(MAX_NAME_TABLE_SIZE * sizeof(namerecord_t*));
    }
}

/*Each subsequent use of the name cause a lookup operation.*/
int Lookup (namerecord_t* retrieval)
{
    int i;
    for(i = 0; i < curTableCount; i++)
    {
        if(strcmp(retrieval->name, table[i]->name) == 0)
        {
            return i;
        }
    }

    return -1;
}

/*When a declaration is processed the name is inserted into the
	the symbol table. If the programming language does not require
	declarations, then the name is inserted when the first occurrence
	of the name is found.*/
void Enter (namerecord_t* insert)
{
    namerecord_t* temp = NULL;
    int loc;
    loc = Lookup(insert);
    if(loc == -1)
    {
        table[curTableCount]->kind = insert->kind;
        strncpy(table[curTableCount]->name, insert->name, cmax -1);
	    table[curTableCount]->val = insert->val;
	    table[curTableCount]->level = insert->level;
	    table[curTableCount]->adr = insert->adr;

        curTableCount++;
    }
}

void ThrowError(int result)
{
    switch(result)
    {
    case notletter:
        {
            fprintf(stderr, "Variable does not start with letter.");
            break;
        }
    case numover:
        {
            fprintf(stderr, "Number too long.");
            break;
        }
    case identover:
        {
            fprintf(stderr, "Name too long.");
            break;
        }
    case invalidsym:
        {
            fprintf(stderr, "Invalid symbols.");
            break;
        }
    default:
        {
            fprintf(stderr, "Unknown Error");
            break;
        }
    }
}

int TokenCompare(char token[tempmax], char compare[tempmax])
{
    if(strcmp(token, compare) == 0)
        return 1;
    else
        return 0;
}

void DetermineTokenType(int position, char token[tempmax])
{
    int type = 0;
    int result;
    int i;
    for(i = 1; i <= TOKEN_TYPE_COUNT; i++)
    {
        if( !( i == identsym || i == numbersym ) )
        {
            if( TokenCompare(token, tokenLookup[i]) )
                type = i;
        }
    }

    if(type == 0)
    {
        result = IdentifierCheck(token);
        if(result == valid)
        {
            type = identsym;
            namerecord_t* temp = (namerecord_t*)malloc(1*sizeof(namerecord_t));
            temp->kind = type;
            strncpy (temp->name, token, cmax-1);
    	    temp->level = curLevel;
    	    temp->adr = position;

            Enter(temp);
        }
        /*else if( result == array)
        {
            type = identsym;
            namerecord_t* temp = (namerecord_t*)malloc(1*sizeof(namerecord_t));
            temp->kind = type;
            strncpy (temp->name, token, cmax-1);
            temp->val = GetArrayValue(token);
            temp->level = curLevel;
    	    temp->adr = position;

            Enter(temp);
        }*/
        else if( result == notletter )
        {
            result = NumberCheck(token);
            if( result == valid )
                type = numbersym;
        }
    }

    if(type == 0)
        ThrowError(result);
    else
        tokenType[position] = type;

    if(type == beginsym)
        curLevel++;
    if(type == endsym)
        curLevel--;
}
